/**
 * 面试题02：实现 Singleton 模式
 */
public class Offer_02 {
    public static void main(String[] args) {
        Singleton a = Singleton.getInstance();
        Singleton b = Singleton.getInstance();
        System.out.println(a == b); // true
    }
}

/**
 * 单例模式：双重校验锁
 */
class Singleton {
    // volatile 关键字很有必要，防止指令重排
    private static volatile Singleton uniqueInstance;

    private Singleton() {
    }

    public static Singleton getInstance() {
        // 先判断对象是否已经实例化过，没有的话才进入加锁代码
        if (uniqueInstance == null) {
            // 类对象加锁
            synchronized (Singleton.class) {
                // 对象为空才去创建（懒加载）
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
    /*
     * uniqueInstance = new Singleton(); 分为三步执行：
     * 
     * 1. 为 uniqueInstance 分配内存空间
     * 
     * 2. 初始化 uniqueInstance
     * 
     * 3. 将 uniqueInstance 指向分配的内存地址
     * 
     * 由于 JVM 具有指令重排的特性，执行顺序有可能变成 1 -> 3 -> 2。
     * 指令重排在单线程环境下不会出现问题，但是在多线程环境下会导致一个线程获得还没有初始化的实例。
     * 
     * 使用 volatile 关键字可以禁止指令重排
     */
}